home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / quicktime / quicktime vr / addvractions / addvractions.c < prev    next >
Encoding:
Text File  |  2000-06-23  |  16.5 KB  |  584 lines

  1. //////////
  2. //
  3. //    File:        AddVRActions.c
  4. //
  5. //    Contains:    Sample code for adding wired actions to a QuickTime VR movie.
  6. //
  7. //    Written by:    Tim Monroe
  8. //                Based on existing code by Bill Wright.
  9. //
  10. //    Copyright:    © 1999 by Apple Computer, Inc., all rights reserved.
  11. //
  12. //    Change History (most recent first):
  13. //
  14. //       <2>         07/15/99    rtm        added Endian macros; runs fine on both Mac and Windows;
  15. //                                    added comments; removed reliance on AtomUtilities.c and
  16. //                                    FileUtilities.c
  17. //       <1>         07/14/99    rtm        first file from bw; revised to sample code coding style
  18. //       
  19. //
  20. //    This file contains some sample code that adds a few wired actions to a QuickTime VR movie.
  21. //    Currently you can add two kinds of wired actions to VR movies: (1) actions that are global
  22. //    to a particular node and (2) actions associated with a particular hot spot in a node. An
  23. //    example of a node-specific action might be setting the pan and tilt angles that are used
  24. //    when the user first enters the node. An example of a hot-spot-specific action might be playing
  25. //    a sound when the mouse is moved over the hot spot.
  26. //
  27. //    All currently-supported QTVR wired actions are specific to some particular node, so the atom
  28. //    containers implementing the actions are placed in the node information atom container that is
  29. //    contained in the media sample for that node in the QTVR track. (See the book Virtual Reality
  30. //    Programming With QuickTime VR 2.x for complete information on the format of VR movie files.)
  31. //    So our job here boils down to finding a media sample in the QTVR track, constructing some atom
  32. //    containers for our desired actions, placing those action containers into the appropriate place
  33. //    in the media sample, and then writing the modified media sample back into the QTVR track. We
  34. //    also need to put an atom into the media property atom container to enable wired action processing.
  35. //
  36. //    For complete information about wired actions, see the chapter "Wired Sprites" in the book
  37. //    Programming With QuickTime Sprites.
  38. //
  39. //////////
  40.  
  41. #include "AddVRActions.h"
  42.  
  43.  
  44. //////////
  45. //
  46. // main/WinMain 
  47. // The main function for this application.
  48. //
  49. //////////
  50.  
  51. #if TARGET_OS_MAC
  52. void main (void)
  53. #elif TARGET_OS_WIN32
  54. int CALLBACK WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR theCmdLine, int nCmdShow)
  55. #endif
  56. {
  57.     OSType                    myTypeList[1] = {MovieFileType};
  58.     StandardFileReply        myReply;
  59.     OSErr                    myErr = noErr;
  60.  
  61. #if TARGET_OS_WIN32
  62.     InitializeQTML(0L);                                            // initialize QuickTime Media Layer
  63. #endif    
  64.  
  65. #if TARGET_OS_MAC
  66.     MaxApplZone();                                                // init everything
  67.     InitGraf(&qd.thePort);
  68.     InitFonts();
  69.     FlushEvents(everyEvent, 0);
  70.     InitWindows();
  71.     InitMenus();
  72.     InitDialogs(NULL);
  73.     TEInit();
  74.     InitCursor();
  75. #endif    
  76.     
  77.     myErr = EnterMovies();
  78.     if (myErr != noErr)
  79.         goto bail;
  80.  
  81.     // elicit a movie file from the user
  82.     StandardGetFile(NULL, 1, myTypeList, &myReply);
  83.  
  84.     // add some wired actions to it, if it's a QuickTime VR movie
  85.     if (myReply.sfGood)
  86.         AddVRAct_AddWiredActionsToQTVRMovie(&myReply.sfFile);
  87.         
  88. bail:
  89.     ExitMovies();
  90.  
  91. #if TARGET_OS_WIN32
  92.     // terminate the QuickTime Media Layer
  93.     TerminateQTML();
  94.     return(1);
  95. #endif    
  96.  
  97. #if TARGET_OS_MAC
  98.     return;
  99. #endif    
  100. }
  101.  
  102.  
  103. //////////
  104. //
  105. // AddVRAct_GetFirstHotSpot
  106. // Return, through the theHotSpotID parameter, the ID of the first hot spot in the specified atom container
  107. // (which is assumed to be a node information atom container).
  108. //
  109. // The returned ID is not necessarily the numerically-least ID; it's just the ID of the first hot spot atom
  110. // in the atom container.
  111. //
  112. //////////
  113.  
  114. static OSErr AddVRAct_GetFirstHotSpot (Handle theSample, long *theHotSpotID)
  115. {
  116.     QTAtom            myHotSpotParentAtom = 0;
  117.     QTAtom            myHotSpotAtom = 0;
  118.     OSErr            myErr = noErr;
  119.     
  120.     *theHotSpotID = 0;
  121.  
  122.     myHotSpotParentAtom = QTFindChildByIndex(theSample, kParentAtomIsContainer, kQTVRHotSpotParentAtomType, kIndexOne, NULL);
  123.     if (myHotSpotParentAtom != 0)
  124.         myHotSpotAtom = QTFindChildByIndex(theSample, myHotSpotParentAtom, kQTVRHotSpotAtomType, kIndexOne, theHotSpotID);
  125.     
  126.     return(myErr);
  127. }
  128.  
  129.  
  130. //////////
  131. //
  132. // AddVRAct_CreateHotSpotActionContainer
  133. // Return, through the theActions parameter, an atom container that contains a hot spot action.
  134. //
  135. // Here we set the pan angle to 10.0 degrees when the hot spot is clicked.
  136. //
  137. //////////
  138.  
  139. static OSErr AddVRAct_CreateHotSpotActionContainer (QTAtomContainer *theActions)
  140. {
  141.     
  142.     QTAtom            myEventAtom = 0;
  143.     QTAtom            myActionAtom = 0;
  144.     long            myAction;
  145.     float            myPanAngle;
  146.     OSErr            myErr = noErr;
  147.     
  148.     myErr = QTNewAtomContainer(theActions);
  149.     if (myErr != noErr)
  150.         goto bail;
  151.     
  152.     myErr = QTInsertChild(*theActions, kParentAtomIsContainer, kQTEventType, kQTEventMouseClick, kIndexOne, kZeroDataLength, NULL, &myEventAtom);
  153.     if (myErr != noErr)
  154.         goto bail;
  155.     
  156.     myErr = QTInsertChild(*theActions, myEventAtom, kAction, kIndexOne, kIndexOne, kZeroDataLength, NULL, &myActionAtom);
  157.     if (myErr != noErr)
  158.         goto bail;
  159.     
  160.     myAction = EndianS32_NtoB(kActionQTVRSetPanAngle);
  161.     myErr = QTInsertChild(*theActions, myActionAtom, kWhichAction, kIndexOne, kIndexOne, sizeof(long), &myAction, NULL);
  162.     if (myErr != noErr)
  163.         goto bail;
  164.     
  165.     myPanAngle = 10.0;
  166.     AddVRAct_ConvertFloatToBigEndian(&myPanAngle);
  167.     myErr = QTInsertChild(*theActions, myActionAtom, kActionParameter, kIndexOne, kIndexOne, sizeof(float), &myPanAngle, NULL);
  168.     if (myErr != noErr)
  169.         goto bail;
  170.         
  171. bail:
  172.     return(myErr);
  173. }
  174.  
  175.  
  176. //////////
  177. //
  178. // AddVRAct_CreateFrameLoadedActionContainer
  179. // Return, through the theActions parameter, an atom container that contains a frame-loaded event action.
  180. //
  181. // Here we set the pan angle to 180.0 degrees.
  182. //
  183. //////////
  184.  
  185. static OSErr AddVRAct_CreateFrameLoadedActionContainer (QTAtomContainer *theActions)
  186. {
  187.     QTAtom            myEventAtom = 0;
  188.     QTAtom            myActionAtom = 0;
  189.     long            myAction;
  190.     float            myPanAngle;
  191.     OSErr            myErr = noErr;
  192.     
  193.     myErr = QTNewAtomContainer(theActions);
  194.     if (myErr != noErr)
  195.         goto bail;
  196.     
  197.     myErr = QTInsertChild(*theActions, kParentAtomIsContainer, kQTEventFrameLoaded, kIndexOne, kIndexOne, kZeroDataLength, NULL, &myEventAtom);
  198.     if (myErr != noErr)
  199.         goto bail;
  200.     
  201.     myErr = QTInsertChild(*theActions, myEventAtom, kAction, kIndexOne, kIndexOne, kZeroDataLength, NULL, &myActionAtom);
  202.     if (myErr != noErr)
  203.         goto bail;
  204.     
  205.     myAction = EndianS32_NtoB(kActionQTVRSetPanAngle);
  206.     myErr = QTInsertChild(*theActions, myActionAtom, kWhichAction, kIndexOne, kIndexOne, sizeof(long), &myAction, NULL);
  207.     if (myErr != noErr)
  208.         goto bail;
  209.     
  210.     myPanAngle = 180.0;
  211.     AddVRAct_ConvertFloatToBigEndian(&myPanAngle);
  212.     myErr = QTInsertChild(*theActions, myActionAtom, kActionParameter, kIndexOne, kIndexOne, sizeof(float), &myPanAngle, NULL);
  213.     if (myErr != noErr)
  214.         goto bail;    
  215.         
  216. bail:
  217.     return(myErr);
  218. }    
  219.  
  220.  
  221. //////////
  222. //
  223. // AddVRAct_SetFrameLoadedWiredActions
  224. // Set the specified actions to be a frame-loaded action. If theActions is NULL, remove any existing
  225. // frame-loaded action from theSample.
  226. //
  227. // The theSample parameter is assumed to be a node information atom container; any actions that are global
  228. // to the node should be inserted at the root level of this atom container; in addition, the container type
  229. // should be the same as the event type and should have an atom ID of 1.
  230. //
  231. //////////
  232.  
  233. static OSErr AddVRAct_SetFrameLoadedWiredActions (Handle theSample, QTAtomContainer theActions)
  234. {
  235.     QTAtom            myEventAtom = 0;
  236.     QTAtom            myTargetAtom = 0;
  237.     OSErr            myErr = noErr;
  238.     
  239.     // look for a frame-loaded action atom in the specified actions atom container
  240.     if (theActions != NULL)
  241.         myEventAtom = QTFindChildByID(theActions, kParentAtomIsContainer, kQTEventFrameLoaded, kIndexOne, NULL);
  242.             
  243.     // look for a frame-loaded action atom in the node information atom container
  244.     myTargetAtom = QTFindChildByID(theSample, kParentAtomIsContainer, kQTEventFrameLoaded, kIndexOne, NULL);
  245.     if (myTargetAtom != 0) {
  246.         // if there is already a frame-loaded event atom in the node information atom container,
  247.         // then either replace it with the one we were passed or remove it
  248.         if (theActions != NULL)
  249.             myErr = QTReplaceAtom(theSample, myTargetAtom, theActions, myEventAtom);    
  250.         else
  251.             myErr = QTRemoveAtom(theSample, myTargetAtom);    
  252.     } else {
  253.         // there is no frame-loaded event atom in the node information atom container,
  254.         // so add in the one we were passed
  255.         if (theActions != NULL)
  256.             myErr = QTInsertChildren(theSample, kParentAtomIsContainer, theActions);
  257.     }
  258.         
  259.     return(myErr);
  260. }
  261.  
  262.  
  263. //////////
  264. //
  265. // AddVRAct_SetWiredActionsToHotSpot
  266. // Set the specified actions to be a hot-spot action. If theActions is NULL, remove any existing
  267. // hot-spot actions for the specified hot spot from theSample.
  268. //
  269. //////////
  270.  
  271. static OSErr AddVRAct_SetWiredActionsToHotSpot (Handle theSample, long theHotSpotID, QTAtomContainer theActions)
  272. {
  273.     QTAtom            myHotSpotParentAtom = 0;
  274.     QTAtom            myHotSpotAtom = 0;
  275.     short            myCount,
  276.                     myIndex;
  277.     OSErr            myErr = paramErr;
  278.     
  279.     myHotSpotParentAtom = QTFindChildByIndex(theSample, kParentAtomIsContainer, kQTVRHotSpotParentAtomType, kIndexOne, NULL);
  280.     if (myHotSpotParentAtom == NULL)
  281.         goto bail;
  282.     
  283.     myHotSpotAtom = QTFindChildByID(theSample, myHotSpotParentAtom, kQTVRHotSpotAtomType, theHotSpotID, NULL);
  284.     if (myHotSpotAtom == NULL)
  285.         goto bail;
  286.     
  287.     // see how many events are already associated with the specified hot spot
  288.     myCount = QTCountChildrenOfType(theSample, myHotSpotAtom, kQTEventType);
  289.     
  290.     for (myIndex = myCount; myIndex > 0; myIndex--) {
  291.         QTAtom        myTargetAtom = 0;
  292.         
  293.         // remove all the existing events
  294.         myTargetAtom = QTFindChildByIndex(theSample, myHotSpotAtom, kQTEventType, myIndex, NULL);
  295.         if (myTargetAtom != 0) {
  296.             myErr = QTRemoveAtom(theSample, myTargetAtom);
  297.             if (myErr != noErr)
  298.                 goto bail;
  299.         }
  300.     }
  301.     
  302.     if (theActions) {
  303.         myErr = QTInsertChildren(theSample, myHotSpotAtom, theActions);
  304.         if (myErr != noErr)
  305.             goto bail;
  306.     }
  307.     
  308. bail:
  309.     return(myErr);
  310. }
  311.  
  312.  
  313. //////////
  314. //
  315. // AddVRAct_WriteMediaPropertyAtom
  316. // Add a media property action to the specified media.
  317. //
  318. // We assume that the data passed to us through the theProperty parameter is big-endian.
  319. //
  320. //////////
  321.  
  322. static OSErr AddVRAct_WriteMediaPropertyAtom (Media theMedia, long thePropertyID, long thePropertySize, void *theProperty)
  323. {
  324.     QTAtomContainer        myPropertyAtom = NULL;
  325.     QTAtom                myAtom = 0;
  326.     OSErr                myErr = noErr;
  327.  
  328.     // get the current media property atom
  329.     myErr = GetMediaPropertyAtom(theMedia, &myPropertyAtom);
  330.     if (myErr != noErr)
  331.         goto bail;
  332.     
  333.     // if there isn't one yet, then create one
  334.     if (myPropertyAtom == NULL) {
  335.         myErr = QTNewAtomContainer(&myPropertyAtom);
  336.         if (myErr != noErr)
  337.             goto bail;
  338.     }
  339.  
  340.     // see if there is an existing atom of the specified type; if not, then create one
  341.     myAtom = QTFindChildByID(myPropertyAtom, kParentAtomIsContainer, thePropertyID, kIndexOne, NULL);
  342.     if (myAtom == NULL) {
  343.         myErr = QTInsertChild(myPropertyAtom, kParentAtomIsContainer, thePropertyID, kIndexOne, kIndexZero, kZeroDataLength, NULL, &myAtom);
  344.         if ((myErr != noErr) || (myAtom == NULL))
  345.             goto bail;
  346.     }
  347.     
  348.     // set the data of the specified atom to the data passed in
  349.     myErr = QTSetAtomData(myPropertyAtom, myAtom, thePropertySize, (Ptr)theProperty);
  350.     if (myErr != noErr)
  351.         goto bail;
  352.  
  353.     // write the new atom data out to the media property atom
  354.     myErr = SetMediaPropertyAtom(theMedia, myPropertyAtom);
  355.  
  356. bail:
  357.     if (myPropertyAtom != NULL)
  358.         myErr = QTDisposeAtomContainer(myPropertyAtom);
  359.     
  360.     return(myErr);
  361. }
  362.  
  363.  
  364. //////////
  365. //
  366. // AddVRAct_AddWiredActionsToQTVRMovie
  367. // Add some wired actions to the specified QTVR movie.
  368. //
  369. // Wired actions are added to a QTVR movie by adding atom containers in the appropriate locations.
  370. //
  371. //////////
  372.  
  373. static void AddVRAct_AddWiredActionsToQTVRMovie (FSSpec *theFSSpec)
  374. {    
  375.     short                            myResID = 0;
  376.     short                            myResRefNum = -1;
  377.     Movie                            myMovie = NULL;
  378.     Track                            myTrack = NULL;
  379.     Media                            myMedia = NULL;
  380.     TimeValue                        myTrackOffset;
  381.     TimeValue                        myMediaTime;
  382.     TimeValue                        mySampleDuration;
  383.     TimeValue                        mySelectionDuration;
  384.     TimeValue                        myNewMediaTime;
  385.     QTVRSampleDescriptionHandle        myQTVRDesc = NULL;
  386.     Handle                            mySample = NULL;
  387.     short                            mySampleFlags;
  388.     Fixed                             myTrackEditRate;
  389.     QTAtomContainer                    myActions = NULL;    
  390.     Boolean                            myHasActions;
  391.     long                            myHotSpotID = 0L;                        
  392.     OSErr                            myErr = noErr;
  393.     
  394.     //////////
  395.     //
  396.     // open the movie file and get the QTVR track from the movie
  397.     //
  398.     //////////
  399.     
  400.     // open the movie file for reading and writing
  401.     myErr = OpenMovieFile(theFSSpec, &myResRefNum, fsRdWrPerm);
  402.     if (myErr != noErr)
  403.         goto bail;
  404.  
  405.     myErr = NewMovieFromFile(&myMovie, myResRefNum, &myResID, NULL, newMovieActive, NULL);
  406.     if (myErr != noErr)
  407.         goto bail;
  408.         
  409.     // find the first QTVR track in the movie;
  410.     // this assumes that the movie is a QuickTime VR movie formatted according to version 2.0
  411.     // or later (version 1.0 VR movies don't have a QTVR track)
  412.     myTrack = GetMovieIndTrackType(myMovie, kIndexOne, kQTVRQTVRType, movieTrackMediaType);
  413.     if (myTrack == NULL)
  414.         goto bail;
  415.     
  416.     //////////
  417.     //
  418.     // get first media sample in the QTVR track
  419.     //
  420.     // the QTVR track contains one media sample for each node in the movie;
  421.     // that sample contains a node information atom container, which contains general information
  422.     // about the node (such as its type, its ID, its name, and a list of its hot spots)
  423.     //
  424.     //////////
  425.     
  426.     myMedia = GetTrackMedia(myTrack);
  427.     if (myMedia == NULL)
  428.         goto bail;
  429.     
  430.     myTrackOffset = GetTrackOffset(myTrack);
  431.     myMediaTime = TrackTimeToMediaTime(myTrackOffset, myTrack);
  432.  
  433.     // allocate some storage to hold the sample description for the QTVR track
  434.     myQTVRDesc = (QTVRSampleDescriptionHandle)NewHandle(4);
  435.     if (myQTVRDesc == NULL)
  436.         goto bail;
  437.  
  438.     mySample = NewHandle(0);
  439.     if (mySample == NULL)
  440.         goto bail;
  441.  
  442.     myErr = GetMediaSample(myMedia, mySample, 0, NULL, myMediaTime, NULL, &mySampleDuration, (SampleDescriptionHandle)myQTVRDesc, NULL, 1, NULL, &mySampleFlags);
  443.     if (myErr != noErr)
  444.         goto bail;
  445.  
  446.     //////////
  447.     //
  448.     // add frame-loaded actions
  449.     //
  450.     //////////
  451.     
  452.     // create an action container for frame-loaded actions
  453.     myErr = AddVRAct_CreateFrameLoadedActionContainer(&myActions);
  454.     if (myErr != noErr)
  455.         goto bail;
  456.     
  457.     // add frame-loaded actions to sample
  458.     myErr = AddVRAct_SetFrameLoadedWiredActions(mySample, myActions);
  459.     if (myErr != noErr)
  460.         goto bail;
  461.     
  462.     myErr = QTDisposeAtomContainer(myActions);
  463.     if (myErr != noErr)
  464.         goto bail;
  465.     
  466.     //////////
  467.     //
  468.     // add hot-spot actions
  469.     //
  470.     //////////
  471.     
  472.     // find the first hot spot in the selected node
  473.     myErr = AddVRAct_GetFirstHotSpot(mySample, &myHotSpotID);
  474.     if ((myErr != noErr) || (myHotSpotID == 0))
  475.         goto bail;
  476.     
  477.     // create an action container for hot-spot actions
  478.     myErr = AddVRAct_CreateHotSpotActionContainer(&myActions);
  479.     if (myErr != noErr)
  480.         goto bail;
  481.     
  482.     // add hot-spot actions to sample     
  483.     myErr = AddVRAct_SetWiredActionsToHotSpot(mySample, myHotSpotID, myActions);
  484.     if (myErr != noErr)
  485.         goto bail;
  486.     
  487.     //////////
  488.     //
  489.     // replace sample in media
  490.     //
  491.     //////////
  492.     
  493.     myTrackEditRate = GetTrackEditRate(myTrack, myTrackOffset);
  494.     if (GetMoviesError() != noErr)
  495.         goto bail;
  496.  
  497.     GetTrackNextInterestingTime(myTrack, nextTimeMediaSample | nextTimeEdgeOK, myTrackOffset, fixed1, NULL, &mySelectionDuration);
  498.     if (GetMoviesError() != noErr)
  499.         goto bail;
  500.  
  501.     myErr = DeleteTrackSegment(myTrack, myTrackOffset, mySelectionDuration);
  502.     if (myErr != noErr)
  503.         goto bail;
  504.         
  505.     myErr = BeginMediaEdits(myMedia);
  506.     if (myErr != noErr)
  507.         goto bail;
  508.     
  509.     myErr = AddMediaSample(    myMedia,
  510.                             mySample,
  511.                             0,
  512.                             GetHandleSize(mySample),
  513.                             mySampleDuration,
  514.                             (SampleDescriptionHandle)myQTVRDesc, 
  515.                             1,
  516.                             mySampleFlags,
  517.                             &myNewMediaTime);
  518.     if (myErr != noErr)
  519.         goto bail;
  520.     
  521.     myErr = EndMediaEdits(myMedia);
  522.     if (myErr != noErr)
  523.         goto bail;
  524.     
  525.     // add the media to the track
  526.     myErr = InsertMediaIntoTrack(myTrack, myTrackOffset, myNewMediaTime, mySelectionDuration, myTrackEditRate);
  527.     if (myErr != noErr)
  528.         goto bail;
  529.  
  530.     //////////
  531.     //
  532.     // set the actions property atom, to enable wired action processing
  533.     //
  534.     //////////
  535.             
  536.     myHasActions = true;    // since sizeof(Boolean) == 1, there is no need to swap bytes here
  537.     myErr = AddVRAct_WriteMediaPropertyAtom(myMedia, kSpriteTrackPropertyHasActions, sizeof(Boolean), &myHasActions);
  538.     if (myErr != noErr)
  539.         goto bail;
  540.  
  541.     //////////
  542.     //
  543.     // update the movie resource
  544.     //
  545.     //////////
  546.     
  547.     myErr = UpdateMovieResource(myMovie, myResRefNum, myResID, NULL);
  548.     if (myErr != noErr)
  549.         goto bail;
  550.     
  551.     // close the movie file
  552.     myErr = CloseMovieFile(myResRefNum);
  553.         
  554. bail:
  555.     if (myActions != NULL)
  556.         (void)QTDisposeAtomContainer(myActions);
  557.     
  558.     if (mySample != NULL)
  559.         DisposeHandle(mySample);        
  560.     
  561.     if (myQTVRDesc != NULL)
  562.         DisposeHandle((Handle)myQTVRDesc);        
  563.     
  564.     if (myMovie != NULL)
  565.         DisposeMovie(myMovie);    
  566. }
  567.  
  568.  
  569. //////////
  570. //
  571. // AddVRAct_ConvertFloatToBigEndian
  572. // Convert the specified floating-point number to big-endian format.
  573. //
  574. //////////
  575.  
  576. void AddVRAct_ConvertFloatToBigEndian (float *theFloat)
  577. {
  578.     unsigned long        *myLongPtr;
  579.     
  580.     myLongPtr = (unsigned long *)theFloat;
  581.     *myLongPtr = EndianU32_NtoB(*myLongPtr);
  582. }
  583.  
  584.